package nskhan.controls;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import nskhan.utilities.*;
import nskhan.utilities.Point;
import nskhan.utilities.Stroke;
import nskhan.utilities.Stroke.*;

public class Canvas extends JPanel implements MouseListener,MouseMotionListener, ActionListener {

        public enum Mode {
                drawing, labeling
        }

        private final int pointWidth = 4;

        private static final long serialVersionUID = 1L;

        private StrokeFile strokeFile = new StrokeFile();
        private Stroke stroke = null;
       
        private Stroke selectedStroke;
        private Mode mode = Mode.drawing;       
       
        private JPopupMenu menu = null;
        private JMenuItem menuItemUndefined = new JMenuItem("undefined");
        private JMenuItem menuItemShape = new JMenuItem("shape");
        private JMenuItem menuItemText = new JMenuItem("text");
       
        public Canvas() {
                super();
                initialize();
        }

        private void initialize() {
                this.setLayout(null);
                this.setSize(640, 480);
                
                this.setBackground(Color.white);
                this.setBorder(BorderFactory.createLineBorder(Color.gray, 1));
                
                addMouseListener(this);
                addMouseMotionListener(this);
                                 
                menu = new JPopupMenu();
                menu.add(menuItemUndefined);                    
                menu.add(menuItemShape);
                menu.add(menuItemText);         
               
                menuItemUndefined.addActionListener ( new ActionListener () {
                        public void actionPerformed(ActionEvent e)
                        {                              
                                selectedStroke.setStrokeType(StrokeTypes.UNDEFINED);
                        }
                });
               
                menuItemShape.addActionListener ( new ActionListener () {
                        public void actionPerformed(ActionEvent e)
                        {
                                selectedStroke.setStrokeType(StrokeTypes.SHAPE);
                        }
                });
               
                menuItemText.addActionListener ( new ActionListener () {
                        public void actionPerformed(ActionEvent e)
                        {
                                selectedStroke.setStrokeType(StrokeTypes.TEXT);
                        }
                });
        }

        @Override
        public void paintComponent(Graphics g) { 
        	super.paintComponent(g);
        	redrawCanvas(g);        
        	if(selectedStroke != null) drawStroke(selectedStroke, g);
        }
       
        @Override
        public void mousePressed(MouseEvent e) {
                if (mode == Mode.drawing) {                    
                        int x = e.getX();
                        int y = e.getY();

                        plotPoint(new Point(x, y), getGraphics());
                        
                        stroke = new Stroke();
                        stroke.beginStroke();
                        stroke.addPoint(new Point(x, y));
                }
        }

        @Override
        public void mouseReleased(MouseEvent e) {
                if (mode == Mode.drawing) {
                        int x = e.getX();
                        int y = e.getY();

                        plotPoint(new Point(x, y), getGraphics());

                        stroke.endStroke();
                        strokeFile.addStroke(stroke);
                }
                
                if (mode == Mode.labeling){  
                	
                	int x = e.getX();
                	int y = e.getY();

                	int strokeId = pointBelongsTo(new Point(x, y));
                	if (strokeId == -1)     return;

                	selectedStroke = strokeFile.getStrokeById(strokeId);

                	repaint(); 
                	
                	if(selectedStroke.getStrokeType() == StrokeTypes.SHAPE){
                		menuItemShape.setText(" > " + StrokeTypes.SHAPE);
                		menuItemText.setText(StrokeTypes.TEXT);
                		menuItemUndefined.setText(StrokeTypes.UNDEFINED);
                	}
                	if(selectedStroke.getStrokeType() == StrokeTypes.TEXT){
                		menuItemText.setText(" > " + StrokeTypes.TEXT);
                		menuItemShape.setText(StrokeTypes.SHAPE);
                		menuItemUndefined.setText(StrokeTypes.UNDEFINED);
                	}
                	if(selectedStroke.getStrokeType() == StrokeTypes.UNDEFINED){
                		menuItemUndefined.setText(" > " + StrokeTypes.UNDEFINED);
                		menuItemShape.setText(StrokeTypes.SHAPE);
                		menuItemText.setText(StrokeTypes.TEXT);
                	}
                	menu.show(this,e.getX(),e.getY());
                }
                
        }

        @Override
        public void mouseDragged(MouseEvent e) {
                if (mode == Mode.drawing) {
                        int x = e.getX();
                        int y = e.getY();

                        plotPoint(new Point(x, y), getGraphics());

                        stroke.addPoint(new Point(x, y));
                }
        }
            
        public StrokeFile getStrokeFile() {
                return strokeFile;
        }        

        public void setMode(Mode m) {
                this.mode = m;  
                this.selectedStroke = null;
                repaint();
        }
       
        public void clearCanvas() {
                strokeFile = new StrokeFile();
                selectedStroke = null;
                repaint();
        }
       
        private int pointBelongsTo(Point point) {
                ArrayList<Stroke> strokes = strokeFile.getStrokes();
                for (Stroke s : strokes) {
                        ArrayList<Point> points = s.getPoints();
                        for (Point p : points) {
                                if    (Math.abs(p.getX() - point.getX()) <= pointWidth
                                    && Math.abs(p.getY() - point.getY()) <= pointWidth)
                                        return s.getId();
                        }
                }
                return -1;
        }

        private void drawStroke(Stroke s, Graphics g) {
                ArrayList<Point> points = s.getPoints();
                for (Point p : points) {
                        plotPoint(p, Color.red, g);
                }
        }
        
        private void redrawCanvas(Graphics g) {
        	
        	if(strokeFile != null)
        	{
        		ArrayList<Stroke> strokes = strokeFile.getStrokes();

        		for (Stroke s : strokes) {
        			ArrayList<Point> points = s.getPoints();
        			for (Point p : points) {
        				plotPoint(p, g);
        			}
        		}
        	}
        }

        private void plotPoint(Point p, Graphics g) {
                plotPoint(p, Color.black, g);
        }

        private void plotPoint(Point p, Color c, Graphics g) {
                g.setColor(c);
                g.fillOval(p.getX(), p.getY(), pointWidth, pointWidth);
        }       
       
        @Override
        public void actionPerformed(ActionEvent e) {      
        }
        
        @Override
        public void mouseClicked(MouseEvent e) {              
        }
        
        @Override
        public void mouseMoved(MouseEvent e) {
        }
       
        @Override
        public void mouseEntered(MouseEvent arg0) {
        }

        @Override
        public void mouseExited(MouseEvent arg0) {
        }
}
